#ifndef SLEPDEF_H
#define SLEPDEF_H

typedef enum{
	SlepLeft,
	SlepRight
}slep_side;

typedef enum{
	SlepCompressedSparseColumn, 
	SlepCompressedSparseRow
}slep_sparse_format;

typedef enum{
	SlepSparse,
	SlepDense,
	SlepSymmetric,
	SlepTriangular,
	SlepBanded
}slep_sparsity;

typedef enum{
	SlepTranspose,
	SlepNoTranspose
}slep_transposity;

typedef enum{
	SlepUpper,
	SlepLower,
	SlepBandedUpper,
	SlepBandedLower,
	SlepBandedBoth,
	SlepFull
}slep_uplobanded;

typedef struct{
	double *values;
	int *ColumnsORRows;
	int *Index;
	slep_sparse_format format; 
}sparse_matrix_base; // Zero Based Indexing Compressed-Sparse-Row Format

typedef struct{
	union{
		sparse_matrix_base spmtrx;
		double *mtrx;
	}data;

	/* This has value in {SlepDense, SlepSparse, SlepSymmetric, SlepTriangular} */
	slep_sparsity sparsity;

	/* This has value in {SlepUpper, SlepLower, SlepBandedUpper, SlepBandedLower, SlepBandedBoth, SlepFull} */
	slep_uplobanded uplobanded;

	/* M Rows */
	int m;

	/* N Columns */
	int n;

	/* Leading Dimension (used for Dense Matrices and Symmetric or Banded Matrices) */
	int ld;

	/* Bandwidth */
	int kl;
	int ku;

}slep_matrix;

typedef struct{
	double *values;
	int *index;
	int nnz;
}sparse_vector_base;

typedef struct{
	union{
		sparse_vector_base spvctr;
		double *vctr;
	}data;

	/* This has value in {SlepDense, SlepSparse} */
	slep_sparsity sparsity;

	int inc;
	int dim;
}slep_vector;

slep_matrix* createCSCSlepMatrix(int m, int n, double *values, int *rows, int *columnIndex);
slep_matrix* createCSRSlepMatrix(int m, int n, double *values, int *columns, int *rowIndex);
slep_matrix* createDenseSlepMatrix(int m, int ld, int n, double *values);

slep_matrix* createSymmetricSlepMatrix(int n, double *values, slep_uplobanded uplobanded, int bandwidth);
slep_matrix* createTriangularSlepMatrix(int n, double *values, slep_uplobanded uplobanded, int bandwidth);

slep_vector* createSparseSlepVector(int nnz, int *index, double *values);
slep_vector* createDenseSlepVector(int dimension, double *values, int inc);

int slepFreeVector(slep_vector* vctr);
slep_vector* slepCopyToNewVector(slep_vector* vctr);

int slepFreeMatrix(slep_matrix* mtrx);
int findNnz(slep_matrix* mtrx);

/* Should Implement these methods */
slep_vector* slepAllocDenseVector(int size);
slep_vector* slepAllocSparseVector(int nnz);
slep_matrix* slepAllocDenseMatrix(int m,int n);
slep_matrix* slepAllocSparseMatrix(int m,int nnz);
slep_vector* slepAllocVector(int memory, slep_sparsity sparsity);


/*--------------------------- Copied from Old Slep.h File ---------------------------*/

typedef struct{
	double* x;
	double* funVal;
	double* ValueL;
	char type[20]; // strcpy the type here
	double c; // Used for logistic regression only
	int totIter; // Total Iterations that happened in this call
	int errorCode; // 0 for no error
}FUNVAL;

typedef struct{

	char registry[35];// Registry for set variables(used by internal function only). Don't modify by youself

	
	int m,n;  // dimensions

	//Starting points
	double* x0; // Starting x
	double c0; // Used in Logistic regression only
	double *mt_c0; // Used in mtLogistic regression only - c0 not used here
	double *mc_c0; // Used in mcLogistic regression only - c0 not used here


	int init; // Init takes values 0,1,2,3,4,5 default:2

	// Termination
	int maxIter;
	double tol;
	int tFlag;

	//Normalization
	int nFlag;
	double* mu;
	double* nu;

	//Method
	int method;
	int mFlag;
	int lFlag;

	//Regularization
	int rFlag;
	double rsL2;

	//Group and others
	int* ind;
	int ind_size;
	double q;
	double *gWeights;

	double sWeight[2]; // registry set to 'sWeight'
	int mc_k;
	// 2 more to come

}OPTS;

void initOPTS(OPTS *opt,int m,int n,int k);
void setOPTx0(OPTS *opt,double* x0);
void setOPTc0(OPTS *opt,double c0);
void setOPTmtc0(OPTS *opt,double* mtc0);
void setOPTmu(OPTS *opt,double* mu);
void setOPTnu(OPTS *opt,double* nu);
void setOPTsWeight(OPTS *opt,double* sWeight);
void setOPTind(OPTS* opt,int* ind, int ind_size);
void setOPTq(OPTS* opt,double q);
void setOPTgWeights(OPTS* opt,double *gWeights);
int hasGot(OPTS *opt,char * field);

#endif //SLEPDEF_H